#pragma once
#include "DGeometry.h"

namespace Skill
{
	namespace Ode
	{
		ref class DMatrix4;
		ref class DVector3;
		ref class DSpace;

		namespace Geometries
		{
			///<summary>
			/// Used by the callback heightfield data type to sample a height for a
			/// given cell position.
			///</summary>
			///<param name = "userData"> User data specified when creating the DHeightfieldData </param>			
			///<param name = "x">
			/// x The index of a sample in the local x axis. It is a value
			///	in the range zero to ( nWidthSamples - 1 ).
			///</param>			
			///<param name = "z">
			/// The index of a sample in the local z axis. It is a value
			/// in the range zero to ( nDepthSamples - 1 ).
			///</param>			
			///<return>			
			/// The sample height which is then scaled and offset using the
			/// values specified when the heightfield data was created.
			///</return>						
			public delegate dReal DHeightfieldGetHeight( Object^ userData, int x, int z );

			public ref class DHeightfieldData : DIUnmanagedPointer
			{
				INTERNAL_CLASS_DECLARATION_NO_FINALIZE(DHeightfieldData,dxHeightfieldData);
			internal:
				float* _HeightData;				
				DHeightfieldGetHeight^ _Callback;
				Object^ _UserData;
			private:
				int _Length;
				dReal _Width;
				dReal _Depth;
				int _WidthSamples;
				int _DepthSamples;
				dReal _Scale;
				dReal _Offset;
				dReal _Thickness;
				bool _Wrap;
				dReal _MinHeight;
				dReal _MaxHeight;
			public:
				UNMANAGEDPOINTER_DECLARATION();

				!DHeightfieldData()
				{					
					if(_Free && _This)
					{
						dGeomHeightfieldDataDestroy(_This);
						if(_HeightData)
							delete[] _HeightData;
					}
					_This = nullptr;
					_Free = false;
					_Disposed = true;
				}

				///<summary>
				/// Gets or set height at index (use this method only when created by heightData)
				///</summary>
				property float default[int]
				{
					float get(int index){return _HeightData[index];}
					void set(int index,float value){_HeightData[index] = value;}
				}
				///<summary>
				/// Gets or set height at x,z index (set worke only when created by heightData)
				///</summary>
				property float default[int,int]
				{
					float get(int x,int z)
					{
						if(_HeightData)
							return _HeightData[x * _WidthSamples + z];
						else if(_Callback)
							return _Callback(_UserData,x,z);
						return _MinHeight;
					}
					void set(int x,int z,float value)
					{
						if(_HeightData)
							_HeightData[x * _WidthSamples + z] = value;						
					}
				}

				VALUE_PROP_DECLARATION_GET(int,Length);
				VALUE_PROP_DECLARATION_GET(dReal,Width);
				VALUE_PROP_DECLARATION_GET(dReal,Depth);
				VALUE_PROP_DECLARATION_GET(int,WidthSamples);
				VALUE_PROP_DECLARATION_GET(int,DepthSamples);
				VALUE_PROP_DECLARATION_GET(dReal,Scale);
				VALUE_PROP_DECLARATION_GET(dReal,Offset);
				VALUE_PROP_DECLARATION_GET(dReal,Thickness);
				VALUE_PROP_DECLARATION_GET(bool,Wrap);
				VALUE_PROP_DECLARATION_GET(dReal,MinHeight);
				VALUE_PROP_DECLARATION_GET(dReal,MaxHeight);

				///<summary>
				/// Creates a new empty dHeightfieldDataID.
				/// Configures a DHeightfieldData to use height data in 
				/// single precision floating point format.
				/// Before a dHeightfieldDataID can be used by a geom it must be
				/// configured to specify the format of the height data.
				/// This call specifies that the heightfield data is stored as a rectangular
				/// array of single precision floats representing the height at each
				/// sample point.
				///</summary>
				///<param name = "heightData"> HeightData </param>
				///<param name = "width"> Specifies the total 'width' of the heightfield along
				/// the geom's local x axis. </param>
				///<param name = "depth">depth Specifies the total 'depth' of the heightfield along
				/// the geom's local z axis.</param>
				///<param name = "widthSamples">Specifies the number of vertices to sample
				/// along the width of the heightfield. Each vertex has a corresponding
				/// height value which forms the overall shape.
				/// Naturally this value must be at least two or more.</param>
				///<param name = "depthSamples">Specifies the number of vertices to sample
				/// along the depth of the heightfield.</param>
				///<param name = "scale"> A uniform scale applied to all raw height data.</param>
				///<param name = "offset"> offset An offset applied to the scaled height data.</param>
				///<param name = "thickness"> thickness A value subtracted from the lowest height
				/// value which in effect adds an additional cuboid to the base of the
				/// heightfield. This is used to prevent geoms from looping under the
				/// desired terrain and not registering as a collision. Note that the
				/// thickness is not affected by the scale or offset parameters.</param>
				///<param name = "wrap"> If true the heightfield will infinitely tile in both
				/// directions along the local x and z axes. If zero the heightfield is
				/// bounded from zero to width in the local x axis, and zero to depth in
				/// the local z axis.
				///</param>
				///<return>
				/// A DHeightfieldData for use with dGeomHeightfieldDataBuildCallback,
				/// dGeomHeightfieldDataBuildByte, dGeomHeightfieldDataBuildShort or
				/// dGeomHeightfieldDataBuildFloat.
				///</return>
				DHeightfieldData(array<float>^ heightData,
					dReal width, dReal depth, int widthSamples, int depthSamples,
					dReal scale, dReal offset, dReal thickness, bool wrap )
				{
					_SetPointer(dGeomHeightfieldDataCreate(),true);
					_HeightData = new float[heightData->Length]();
					Marshal::Copy(heightData,0,IntPtr(_HeightData),heightData->Length);
					dGeomHeightfieldDataBuildSingle(_This,_HeightData,0,width,depth,widthSamples,depthSamples,scale,offset,thickness,wrap? 1:0);

					_Length = heightData->Length;
					_Width = width;
					_Depth = depth;
					_WidthSamples = widthSamples;
					_DepthSamples = depthSamples;
					_Scale = scale;
					_Offset = offset;
					_Thickness = thickness;
					_Wrap = wrap? 1:0;

					_MinHeight = System::Single::MinValue;
					_MaxHeight = System::Single::MaxValue;
					_UserData = nullptr;
					_Callback = nullptr;
				}

				///<summary>
				/// Creates a new empty dHeightfieldDataID.
				/// Configures a DHeightfieldData to use a callback to
				/// retrieve height data.
				/// Before a dHeightfieldDataID can be used by a geom it must be
				/// configured to specify the format of the height data.
				/// This call specifies that the heightfield data is stored as a rectangular
				/// array of single precision floats representing the height at each
				/// sample point.
				///</summary>
				///<param name = "userData"> HeightData </param>
				///<param name = "width"> Specifies the total 'width' of the heightfield along
				/// the geom's local x axis. </param>
				///<param name = "depth">depth Specifies the total 'depth' of the heightfield along
				/// the geom's local z axis.</param>
				///<param name = "widthSamples">Specifies the number of vertices to sample
				/// along the width of the heightfield. Each vertex has a corresponding
				/// height value which forms the overall shape.
				/// Naturally this value must be at least two or more.</param>
				///<param name = "depthSamples">Specifies the number of vertices to sample
				/// along the depth of the heightfield.</param>
				///<param name = "scale"> A uniform scale applied to all raw height data.</param>
				///<param name = "offset"> offset An offset applied to the scaled height data.</param>
				///<param name = "thickness"> thickness A value subtracted from the lowest height
				/// value which in effect adds an additional cuboid to the base of the
				/// heightfield. This is used to prevent geoms from looping under the
				/// desired terrain and not registering as a collision. Note that the
				/// thickness is not affected by the scale or offset parameters.</param>
				///<param name = "wrap"> If true the heightfield will infinitely tile in both
				/// directions along the local x and z axes. If zero the heightfield is
				/// bounded from zero to width in the local x axis, and zero to depth in
				/// the local z axis.
				///</param>
				///<return>
				/// A DHeightfieldData for use with dGeomHeightfieldDataBuildCallback,
				/// dGeomHeightfieldDataBuildByte, dGeomHeightfieldDataBuildShort or
				/// dGeomHeightfieldDataBuildFloat.
				///</return>
				DHeightfieldData(Object^ userData, DHeightfieldGetHeight^ callback,
					dReal width, dReal depth, int widthSamples, int depthSamples,
					dReal scale, dReal offset, dReal thickness, bool wrap );

				///<summary>
				/// Manually set the minimum and maximum height bounds.				
				/// This call allows you to set explicit min / max values after initial
				/// creation typically for callback heightfields which default to +/- infinity,
				/// or those whose data has changed. This must be set prior to binding with a
				/// geom, as the the AABB is not recomputed after it's first generation.
				///</summary>
				///<param name = "minHeight"> The new minimum height value. Scale, offset and thickness is then applied.</param>
				///<param name = "maxHeight">The new maximum height value. Scale and offset is then applied. </param>
				///<remarks>
				/// The minimum and maximum values are used to compute the AABB
				/// for the heightfield which is used for early rejection of collisions.
				/// A close fit will yield a more efficient collision check.
				///</remarks>				
				void SetBounds(dReal minHeight, dReal maxHeight )
				{
					_MinHeight = minHeight;
					_MaxHeight = maxHeight;
					dGeomHeightfieldDataSetBounds(_This,minHeight, maxHeight);
				}


				/**
				* @brief Configures a dHeightfieldDataID to use height data in byte format.
				*
				* Before a dHeightfieldDataID can be used by a geom it must be
				* configured to specify the format of the height data.
				* This call specifies that the heightfield data is stored as a rectangular
				* array of bytes (8 bit unsigned) representing the height at each sample point.
				*
				* @param d A new dHeightfieldDataID created by dGeomHeightfieldDataCreate
				*
				* @param pHeightData A pointer to the height data.
				* @param bCopyHeightData When non-zero the height data is copied to an
				* internal store. When zero the height data is accessed by reference and
				* so must persist throughout the lifetime of the heightfield.
				*
				* @param width Specifies the total 'width' of the heightfield along
				* the geom's local x axis.
				* @param depth Specifies the total 'depth' of the heightfield along
				* the geom's local z axis.
				*
				* @param widthSamples Specifies the number of vertices to sample
				* along the width of the heightfield. Each vertex has a corresponding
				* height value which forms the overall shape.
				* Naturally this value must be at least two or more.
				* @param depthSamples Specifies the number of vertices to sample
				* along the depth of the heightfield.
				*
				* @param scale A uniform scale applied to all raw height data.
				* @param offset An offset applied to the scaled height data.
				*
				* @param thickness A value subtracted from the lowest height
				* value which in effect adds an additional cuboid to the base of the
				* heightfield. This is used to prevent geoms from looping under the
				* desired terrain and not registering as a collision. Note that the
				* thickness is not affected by the scale or offset parameters.
				*
				* @param bWrap If non-zero the heightfield will infinitely tile in both
				* directions along the local x and z axes. If zero the heightfield is
				* bounded from zero to width in the local x axis, and zero to depth in
				* the local z axis.
				*
				* @ingroup collide
				*/
				/*ODE_API void dGeomHeightfieldDataBuildByte( dHeightfieldDataID d,
					const unsigned char* pHeightData, int bCopyHeightData,
					dReal width, dReal depth, int widthSamples, int depthSamples,
					dReal scale, dReal offset, dReal thickness,	int bWrap );*/

				/**
				* @brief Configures a dHeightfieldDataID to use height data in short format.
				*
				* Before a dHeightfieldDataID can be used by a geom it must be
				* configured to specify the format of the height data.
				* This call specifies that the heightfield data is stored as a rectangular
				* array of shorts (16 bit signed) representing the height at each sample point.
				*
				* @param d A new dHeightfieldDataID created by dGeomHeightfieldDataCreate
				*
				* @param pHeightData A pointer to the height data.
				* @param bCopyHeightData When non-zero the height data is copied to an
				* internal store. When zero the height data is accessed by reference and
				* so must persist throughout the lifetime of the heightfield.
				*
				* @param width Specifies the total 'width' of the heightfield along
				* the geom's local x axis.
				* @param depth Specifies the total 'depth' of the heightfield along
				* the geom's local z axis.
				*
				* @param widthSamples Specifies the number of vertices to sample
				* along the width of the heightfield. Each vertex has a corresponding
				* height value which forms the overall shape.
				* Naturally this value must be at least two or more.
				* @param depthSamples Specifies the number of vertices to sample
				* along the depth of the heightfield.
				*
				* @param scale A uniform scale applied to all raw height data.
				* @param offset An offset applied to the scaled height data.
				*
				* @param thickness A value subtracted from the lowest height
				* value which in effect adds an additional cuboid to the base of the
				* heightfield. This is used to prevent geoms from looping under the
				* desired terrain and not registering as a collision. Note that the
				* thickness is not affected by the scale or offset parameters.
				*
				* @param bWrap If non-zero the heightfield will infinitely tile in both
				* directions along the local x and z axes. If zero the heightfield is
				* bounded from zero to width in the local x axis, and zero to depth in
				* the local z axis.
				*
				* @ingroup collide
				*/
				/*ODE_API void dGeomHeightfieldDataBuildShort( dHeightfieldDataID d,
					const short* pHeightData, int bCopyHeightData,
					dReal width, dReal depth, int widthSamples, int depthSamples,
					dReal scale, dReal offset, dReal thickness, int bWrap );*/

				

				/**
				* @brief Configures a dHeightfieldDataID to use height data in 
				* double precision floating point format.
				*
				* Before a dHeightfieldDataID can be used by a geom it must be
				* configured to specify the format of the height data.
				* This call specifies that the heightfield data is stored as a rectangular
				* array of double precision floats representing the height at each
				* sample point.
				*
				* @param d A new dHeightfieldDataID created by dGeomHeightfieldDataCreate
				*
				* @param pHeightData A pointer to the height data.
				* @param bCopyHeightData When non-zero the height data is copied to an
				* internal store. When zero the height data is accessed by reference and
				* so must persist throughout the lifetime of the heightfield.
				*
				* @param width Specifies the total 'width' of the heightfield along
				* the geom's local x axis.
				* @param depth Specifies the total 'depth' of the heightfield along
				* the geom's local z axis.
				*
				* @param widthSamples Specifies the number of vertices to sample
				* along the width of the heightfield. Each vertex has a corresponding
				* height value which forms the overall shape.
				* Naturally this value must be at least two or more.
				* @param depthSamples Specifies the number of vertices to sample
				* along the depth of the heightfield.
				*
				* @param scale A uniform scale applied to all raw height data.
				* @param offset An offset applied to the scaled height data.
				*
				* @param thickness A value subtracted from the lowest height
				* value which in effect adds an additional cuboid to the base of the
				* heightfield. This is used to prevent geoms from looping under the
				* desired terrain and not registering as a collision. Note that the
				* thickness is not affected by the scale or offset parameters.
				*
				* @param bWrap If non-zero the heightfield will infinitely tile in both
				* directions along the local x and z axes. If zero the heightfield is
				* bounded from zero to width in the local x axis, and zero to depth in
				* the local z axis.
				*
				* @ingroup collide
				*/
				/*ODE_API void dGeomHeightfieldDataBuildDouble( dHeightfieldDataID d,
					const double* pHeightData, int bCopyHeightData,
					dReal width, dReal depth, int widthSamples, int depthSamples,
					dReal scale, dReal offset, dReal thickness, int bWrap );*/				

			};
		}
	}
}